#include "ImportExport.h"

#include "../../core/Impp.h"
#include <wx/wfstream.h>
#include <wx/txtstrm.h>
#include <wx/filedlg.h>
#include "actions/ImportAction.h"
#include "../StdUserInterfaceModule/UserInterface.h"

namespace Impp
{

IMPLEMENT_DYNAMIC_CLASS(StdImportExportModule, iModule)

ImportExport::ImportExport() :
	wxEvtHandler()
{
	wxXml2::Init();

	//register Event handler
	Impp* impp = Impp::exemplar();
	impp->Connect(wxID_ANY,EVT_IMPP_COMMAND, ImppCommandEventHandler(ImportExport::onImppCommand), NULL, this);

	iUserInterface* ui = impp->getModuleManager()->getUserInterfaceModule();
	if(ui != NULL){
		ui->reigsterMainMenuEntryAdder(STD_IMPORT_EXPORT_MODULE_NAME, ImportExport::addMainMenuItem);
		ui->getIcons()->registerActionIcon(STD_IMPORT_EXPORT_MODULE_NAME, wxT("History"), wxT("icons/chat/history.png"));
	}else{
		wxLogDebug(wxT("ImportExport: No UserInterface found to add MainMenu entry!"));
	}

}

ImportExport::~ImportExport()
{
}


void ImportExport::onImppCommand(ImppCommandEvent & event){
	event.Skip();
	if(event.getModuleName() == STD_IMPORT_EXPORT_MODULE_NAME){
		if(event.getActionName() == STD_IMPORT_EXPORT_MODULE_ACTION_IMPORT){

		   Account* acc = UserInterface::showChooseAccountDialog(wxT("Choose Account"), wxT("Import History to Account:"));

		   if(acc != NULL){
				wxFileDialog fd(NULL, wxT("Choose the file to import"), wxT(""), wxT(""),
								wxT("XML files|*.xml"), wxFD_OPEN);
				if (fd.ShowModal() == wxID_OK){
					importHistory(acc, fd.GetPath());
				}

		   }

		}else if(event.getActionName() == STD_IMPORT_EXPORT_MODULE_ACTION_EXPORT_WHOLE){

			Account* acc = UserInterface::showChooseAccountDialog(wxT("Choose Account"), wxT("Export History from Account:"));

			if(acc != NULL){
			    wxFileDialog fd(NULL, wxT("Choose the file to export to"), wxT(""), wxT(""),
				                    wxT("XML files|*.xml"), wxFD_SAVE);

				if (fd.ShowModal() == wxID_OK){
				   	exportAllHistory(acc, fd.GetPath());
				}
			}
		}
	}
}

void wxCALLBACK ImportExport::addMainMenuItem(iMenu* menu, void* ptr){
	iMenu* historyMenu = menu->appendOrGetSubMenu(wxT("History"), wxT("History"), 70, wxT("Show History"));
	historyMenu->appendItem(STD_IMPORT_EXPORT_MODULE_ACTION_IMPORT, wxT("Import History"), 20, wxT("Import History"));
	historyMenu->appendItem(STD_IMPORT_EXPORT_MODULE_ACTION_EXPORT_WHOLE, wxT("Export whole History"), 30, wxT("Export whole History"));
}

void ImportExport::onIdleEvent(wxIdleEvent & event){
	if(!importStep()){
		event.RequestMore(true);
	}
	event.Skip();
}

bool ImportExport::importStep(){
	if(importSession == NULL){
		return true;
	}


	if(importSession->curMessageNode != wxXml2EmptyNode){
		//wxLogDebug(wxT("looking for messageNode"));

		bool next = true;

		while(next){

	    	if(importSession->curMessageNode.GetType() == wxXML2_ELEMENT_NODE ){
	    		if(importSession->curMessageNode.GetName() == wxT("message")){
	    			/*
	    			 *	Import curMessageNode
	    			 */
	    			HistoryRecord historyRecord;
	    			historyRecord.msg = importSession->curMessageNode.GetNodeContent();

	    			wxXml2Property prop = importSession->curMessageNode.GetProperties();
	    			while (prop != wxXml2EmptyProperty) {
	    				wxString propName = prop.GetName();

	    				if(propName == wxT("out")){
	    					historyRecord.out = prop.GetValue() == wxT("true");
	    				}else if(propName == wxT("time")){
	    					historyRecord.time.ParseFormat(prop.GetValue(), wxT("%d-%m-%Y %H:%M:%S"));
	    				}

	    				prop = prop.GetNext();
	    			}

	    			importSession->messageCount++;
	    			bool exists =  Impp::exemplar()->getDatabase()->doesHistoryEntryExist(importSession->buddy, historyRecord.out, historyRecord.time, historyRecord.msg);

	    			if(!exists){
	    				Impp::exemplar()->getDatabase()->saveInHistoryInSession(historyRecord.time, importSession->buddy, historyRecord.msg, historyRecord.out, db_id(0));
	    				importSession->importedMessagesCount++;
	    			}
	    			//wxLogDebug(wxT("Message parsed: %s"), historyRecord.time.Format(wxT("%d-%m-%Y %H:%M:%S")).c_str());
	    			next = false;
	    		}
	    	}
	    	importSession->curMessageNode = importSession->curMessageNode.GetNext();

			if(importSession->curMessageNode == wxXml2EmptyNode){
				if(importSession->importedMessagesCount != 0){
					wxLogDebug(wxT("recreate!"));
					Impp::exemplar()->getDatabase()->recreateHistorySessions(importSession->buddy);
				}
				return false;
			}
		}
	/*
	 * if no next node
	 */
	}else{
		//wxLogDebug(wxT("looking for buddyNode"));
		bool next = importSession->curBuddyNode != wxXml2EmptyNode;

		while(next){

			/*
			 * No more buddie node -> finished
			 */
			if(importSession->curBuddyNode == wxXml2EmptyNode){
				wxLogDebug(wxT("Parsing finished!"));
				Impp::exemplar()->getActionManager()->deleteAction(importSession->id, true);
				delete importSession;
				importSession = NULL;
				wxApp::GetInstance()->Disconnect(wxEVT_IDLE, wxIdleEventHandler(ImportExport::onIdleEvent), NULL, this);
				return true;
			}

			if(importSession->curBuddyNode.GetType() == wxXML2_ELEMENT_NODE ){
				if(importSession->curBuddyNode.GetName() == wxT("buddy")){
					//Node is a buddy node so get the screenname
					wxString screenName;

					wxXml2Property prop = importSession->curBuddyNode.GetProperties();
					while (prop != wxXml2EmptyProperty) {
						wxString propName = prop.GetName();
						if(propName == wxT("screenname")){
							screenName = prop.GetValue();
							break;
						}
						prop = prop.GetNext();
					}

					if(!screenName.IsEmpty()){
						if((importSession->buddy = Impp::exemplar()->getBuddy(importSession->account->id, screenName))){
							wxLogDebug(wxT("Next Buddy: %s"), importSession->buddy->getName().c_str());

							/*
							 * Suitable node found -> leave and parse messages
							 */
							importSession->curMessageNode = importSession->curBuddyNode.GetFirstChild();
							importSession->importedMessagesCount = 0;
							importSession->messageCount = 0;
							next = false;
						}
					}
				}
			}
			importSession->curBuddyNode = importSession->curBuddyNode.GetNext();
		}
	}
	return false;

}

void ImportExport::importHistory(Account* account, const wxString & file){

		wxApp::GetInstance()->Connect(wxEVT_IDLE, wxIdleEventHandler(ImportExport::onIdleEvent), NULL, this);

		importSession = new ImportSession();
		importSession->account=account;

	    importSession->doc.Load(file);

	    wxXml2Node root = importSession->doc.GetRoot();

	    if(root.GetName() == wxT("log")){

	    	importSession->id = Impp::exemplar()->getActionManager()->insertAction(new ImportAction(account->self));
	    	importSession->curBuddyNode = root.GetFirstChild();

	    }else{
	    	delete importSession;
	    	importSession=NULL;
	    	wxLogError(wxT("Invalid xml file"));
	    }

}

void ImportExport::exportBuddyHistory(Buddy* buddy, const wxString & file){
        wxFileOutputStream fileOut(file);
        wxTextOutputStream out(fileOut);

        writeXmlStart(out);
        writeHistrory(out,buddy,wxDateTime((time_t)0));
        out << wxT("</log>\n");
}

void ImportExport::exportAllHistory(Account* acc, const wxString & file){
        wxFileOutputStream fileOut(file);
        wxTextOutputStream out(fileOut);

        writeXmlStart(out);

        std::vector<Buddy*> buddies;
        Impp::exemplar()->getBuddiesFromAccount(acc->id, buddies);

        for (size_t i = 0; i < buddies.size(); ++i) {
                writeHistrory(out,buddies[i],wxDateTime((time_t)0));
        }
        out << wxT("</log>\n");
}

void ImportExport::writeXmlStart(wxTextOutputStream & out){
        out << wxT("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
        out << wxT("<log>\n");
}

void ImportExport::writeHistrory(wxTextOutputStream & out, Buddy* buddy,const wxDateTime & since){

        Database* db = Impp::exemplar()->getDatabase();
        std::vector<HistoryEntry*> entries;
        db->getHistoryEntries(entries,buddy, since);

        writeBuddyStart(out, buddy);

        std::vector<HistoryEntry*>::iterator dIter(entries.begin());
        for (; dIter != entries.end(); ++dIter) {
                HistoryEntry* e = *dIter;
                writeMessage(out,e->getTime(), e->getMsg(),e->isOutgoing());
                delete e;
        }

        out << wxT("</buddy>\n");
}



void ImportExport::writeMessage(wxTextOutputStream & out, const wxDateTime & time, const wxString & message, bool outgoing){
        out << wxT("<message out=\"") << toBooleanString(outgoing);
        out << wxT("\" time=\"") << time.Format(wxT("%d-%m-%Y %H:%M:%S")) << wxT("\">");
        out << escapeString(message);
        out << wxT("</message>\n");
}

void ImportExport::writeBuddyStart(wxTextOutputStream & out, Buddy* buddy){
        out << wxT("<buddy screenname=\"") << escapeString(buddy->getScreenName()) << wxT("\">\n");
}

wxString ImportExport::toBooleanString(bool b){
        if(b){
                return wxT("true");
        }else{
                return wxT("false");
        }
}

wxString ImportExport::escapeString(wxString str){
        str.Replace(wxT("&"),wxT("&amp;"));
        str.Replace(wxT("<"),wxT("&lt;"));
        str.Replace(wxT(">"),wxT("&gt;"));
        str.Replace(wxT("\""),wxT("&quot;"));
        str.Replace(wxT("'"),wxT("&apos;"));
        return str;
}

}

